﻿using System;
using System.Collections.Generic;
using System.Linq;
using Rodunu.Movies.Services.Interfaces;
using Rodunu.Movies.Services.Models.Movies;
using System.ComponentModel;
using Rodunu.Services.Amazon;
using Rodunu.Services.Amazon.AWS;

namespace Rodunu.Movies.Services.Amazon
{

    public class AmazonService : IAmazonService
    {
        private string _SubscriberID;
        private readonly AWSECommerceServicePortTypeClient _AWSClient;

        #region Constructors

        /// <summary>
        /// Initialize the web service object.
        /// </summary>
        public AmazonService()
        {
            ServiceBinding serviceBinding = new ServiceBinding();
            _AWSClient = new AWSECommerceServicePortTypeClient(serviceBinding.InitBinding(), serviceBinding.InitEndPoint());


            //Rodunu.Services.Amazon.AWS.
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Used to search the Amazon Web Service for results based on keyword.
        /// </summary>
        /// <param name="keyword">Search term to use.</param>
        /// <param name="PageNumber">The page number to search.</param>
        /// <param name="TotalPages">Total number of pages returned by the service.</param>
        /// <param name="firstRun">Used for recursive calling. (true if calling initially)</param>
        /// <param name="Titles">The collection to pass when using recursively. (null if calling initially)</param>
        /// <returns>A collection of movie titles.</returns>
        private IList<Title> SearchWebService(string keyword, int PageNumber, int TotalPages, bool firstRun, IList<Title> Titles, bool withTitle)
        {
            ItemSearchResponse response;

            if (PageNumber == 0)
            {
                response = GetResponse(keyword, 0, withTitle);
                Titles = new List<Title>();
            }
            else
            {
                response = GetResponse(keyword, PageNumber, withTitle);
            }

            if (response != null)
            {
                for (int x = 0; x < response.Items.Length; x++)
                {
                    if (firstRun)
                    {
                        TotalPages = Convert.ToInt32(response.Items[x].TotalPages);
                    }

                    if (TotalPages - PageNumber > 0)
                    {
                        SearchWebService(keyword, PageNumber + 1, TotalPages, false, Titles, withTitle);
                    }

                    if (response.Items[x] == null || response.Items[x].Item == null) continue;

                    for (int i = 0; i < response.Items[x].Item.Length; i++)
                    {
                        Title movieTitle = NewTitle(response, x, i);
                        Titles.Add(movieTitle);
                    }
                }
            }

            return Titles;
        }

        /// <summary>
        /// Gets the ItemSearchResponse from the Amazon Web Service
        /// </summary>
        /// <param name="keyword">Search term to use.</param>
        /// <param name="PageNumber">The page number to search on.</param>
        /// <returns>Search response from Amazon Web Service</returns>
        private ItemSearchResponse GetResponse(string keyword, int PageNumber, bool withPlot)
        {
            ItemSearch search = new ItemSearch { AssociateTag = SubscriberID, AWSAccessKeyId = SubscriberID };

            const string index = "DVD";

            ItemSearchRequest request = new ItemSearchRequest
            {
                ResponseGroup = new[] { "ItemAttributes", "Images", "ItemIds" },
                SearchIndex = index,
                Keywords = keyword,
            };
            if (withPlot)
            {
                request.ResponseGroup[request.ResponseGroup.Length - 1] = "EditorialReview";
            }

            if (PageNumber > 0)
            {
                request.ItemPage = PageNumber.ToString();
            }

            search.Request = new[] { request };

            if (_AWSClient != null)
            {
                ItemSearchResponse response = _AWSClient.ItemSearch(search);

                return response;
            }

            return null;
        }

        /// <summary>
        /// Add actors to generic IList of type Role
        /// </summary>
        /// <param name="actors">string[]</param>
        /// <returns>Generic IList of real names of actors</returns>
        private static IList<Role> AddActors(IEnumerable<string> actors)
        {
            IList<Role> cast = new List<Role>();

            if (actors != null)
            {
                foreach (var s in actors)
                {
                    cast.Add(new Role(string.Empty, s));
                }
            }

            return cast;
        }

        /// <summary>
        /// Add directors to generic IList of type Director
        /// </summary>
        /// <param name="directors">string[]</param>
        /// <returns>Generic IList of real names of directors</returns>
        private static IList<Director> AddDirectors(IEnumerable<string> directors)
        {
            IList<Director> titleDirectors = new List<Director>();

            if (directors != null)
            {
                foreach (var s in directors)
                {
                    titleDirectors.Add(new Director(s));
                }
            }
            return titleDirectors;
        }

        /// <summary>
        /// Add languages to generic IList of type Language
        /// </summary>
        /// <param name="languages">ItemAttributesLanguage[]</param>
        /// <returns>Generic IList of langauges</returns>
        private static IList<Language> AddLanguages(IEnumerable<ItemAttributesLanguage> languages)
        {
            IList<Language> movieLanguages = new List<Language>();

            if (languages != null)
            {
                foreach (ItemAttributesLanguage language in languages)
                {
                    Language movieLanguage = new Language();
                    movieLanguage.Name = language.Name ?? string.Empty;
                    movieLanguage.Type = language.Type ?? string.Empty;
                    movieLanguage.AudioFormat = language.AudioFormat ?? string.Empty;
                    movieLanguages.Add(movieLanguage);
                }
            }
            return movieLanguages;
        }

        /// <summary>
        /// Gets the plot for the movie
        /// </summary>
        /// <param name="editorialReviews">EditorialReview[]</param>
        /// <returns>Movie plot</returns>
        private static string GetProductDescription(IEnumerable<EditorialReview> editorialReviews)
        {
            if (editorialReviews == null) return string.Empty;
            return (from review in editorialReviews select review).First().Content ?? string.Empty;
        }

        /// <summary>
        /// Creates a new movie title from values within the ItemSearchResponse object
        /// </summary>
        /// <param name="response">Object containing response values</param>
        /// <param name="indexTop">Index for the top level array</param>
        /// <param name="indexBottom">Index for the bottom level array</param>
        /// <returns>New Title</returns>
        private static Title NewTitle(ItemSearchResponse response, int indexTop, int indexBottom)
        {
            Title movieTitle = new Title
                                   {
                                       Cast = AddActors(response.Items[indexTop].Item[indexBottom].ItemAttributes.Actor),
                                       Amazon = (response.Items[indexTop].Item[indexBottom].DetailPageURL ?? string.Empty),
                                       ReleaseDate = response.Items[indexTop].Item[indexBottom].ItemAttributes.ReleaseDate,
                                       Directors = AddDirectors(response.Items[indexTop].Item[indexBottom].ItemAttributes.Director),
                                       Genres = new List<string> { response.Items[indexTop].Item[indexBottom].ItemAttributes.Genre },
                                       Languages = AddLanguages(response.Items[indexTop].Item[indexBottom].ItemAttributes.Languages),
                                       Name = response.Items[indexTop].Item[indexBottom].ItemAttributes.Title,
                                       Plot = GetProductDescription(response.Items[indexTop].Item[indexBottom].EditorialReviews),
                                       MPAARating = response.Items[indexTop].Item[indexBottom].ItemAttributes.AudienceRating,
                                       AmazonASIN = response.Items[indexTop].Item[indexBottom].ASIN
                                   };
            
            movieTitle.ReleaseDate = response.Items[indexTop].Item[indexBottom].ItemAttributes.ReleaseDate;
            
            movieTitle.TagLine = response.Items[indexTop].Item[indexBottom].ItemAttributes.UPC;

            if (response.Items[indexTop].Item[indexBottom].ItemAttributes.RunningTime != null)
            {
                movieTitle.Runtime.Units = response.Items[indexTop].Item[indexBottom].ItemAttributes.RunningTime.Units ?? string.Empty;
                movieTitle.Runtime.Value = response.Items[indexTop].Item[indexBottom].ItemAttributes.RunningTime.Value;
            }

            movieTitle.Studio = response.Items[indexTop].Item[indexBottom].ItemAttributes.Studio;
            movieTitle.AddToAmazonWishList = response.Items[indexTop].Item[indexBottom].ItemLinks[3].URL;

            if (response.Items[indexTop].Item[indexBottom].LargeImage != null)
            {
                movieTitle.LargeImageUrl = response.Items[indexTop].Item[indexBottom].LargeImage.URL ?? string.Empty;
            }
            if (response.Items[indexTop].Item[indexBottom].MediumImage != null)
            {
                movieTitle.MediumImageUrl = response.Items[indexTop].Item[indexBottom].MediumImage.URL ?? string.Empty;
            }
            if (response.Items[indexTop].Item[indexBottom].SmallImage != null)
            {
                movieTitle.SmallImageUrl = response.Items[indexTop].Item[indexBottom].SmallImage.URL ?? string.Empty;
            }
            return movieTitle;
        }

        private static Title NewTitleByItem(ItemLookupResponse response, int indexTop, int indexBottom)
        {
            Title movieTitle = new Title
            {
                Cast = AddActors(response.Items[indexTop].Item[indexBottom].ItemAttributes.Actor),
                Amazon = (response.Items[indexTop].Item[indexBottom].DetailPageURL ?? string.Empty),
                ReleaseDate = response.Items[indexTop].Item[indexBottom].ItemAttributes.ReleaseDate,
                Directors = AddDirectors(response.Items[indexTop].Item[indexBottom].ItemAttributes.Director),
                Genres = new List<string> { response.Items[indexTop].Item[indexBottom].ItemAttributes.Genre },
                Languages = AddLanguages(response.Items[indexTop].Item[indexBottom].ItemAttributes.Languages),
                Name = response.Items[indexTop].Item[indexBottom].ItemAttributes.Title,
                Plot = GetProductDescription(response.Items[indexTop].Item[indexBottom].EditorialReviews),
                MPAARating = response.Items[indexTop].Item[indexBottom].ItemAttributes.AudienceRating,
                AmazonASIN = response.Items[indexTop].Item[indexBottom].ASIN
            };

            movieTitle.ReleaseDate = response.Items[indexTop].Item[indexBottom].ItemAttributes.ReleaseDate;

            movieTitle.TagLine = response.Items[indexTop].Item[indexBottom].ItemAttributes.UPC;

            if (response.Items[indexTop].Item[indexBottom].ItemAttributes.RunningTime != null)
            {
                movieTitle.Runtime.Units = response.Items[indexTop].Item[indexBottom].ItemAttributes.RunningTime.Units ?? string.Empty;
                movieTitle.Runtime.Value = response.Items[indexTop].Item[indexBottom].ItemAttributes.RunningTime.Value;
            }

            movieTitle.Studio = response.Items[indexTop].Item[indexBottom].ItemAttributes.Studio;

            if (response.Items[indexTop].Item[indexBottom].LargeImage != null)
            {
                movieTitle.LargeImageUrl = response.Items[indexTop].Item[indexBottom].LargeImage.URL ?? string.Empty;
            }
            if (response.Items[indexTop].Item[indexBottom].MediumImage != null)
            {
                movieTitle.MediumImageUrl = response.Items[indexTop].Item[indexBottom].MediumImage.URL ?? string.Empty;
            }
            if (response.Items[indexTop].Item[indexBottom].SmallImage != null)
            {
                movieTitle.SmallImageUrl = response.Items[indexTop].Item[indexBottom].SmallImage.URL ?? string.Empty;
            }

            movieTitle.AddToAmazonWishList = response.Items[indexTop].Item[indexBottom].ItemLinks[3].URL;
            
            return movieTitle;
        }

        #endregion

        #region Private Properties

        /// <summary>
        /// returns the Amazon Web Services subcription string
        /// </summary>
        private string SubscriberID
        {
            get
            {
                if (string.IsNullOrEmpty(_SubscriberID))
                {
                    // Retrieve Amazon subscriber id from configuration file.
                    //_SubscriberID = ConfigurationManager.AppSettings["AmazonSubscriberID"].ToString();
                    _SubscriberID = "0T4XHQ55A2QQMF61C002";
                }
                return _SubscriberID;
            }
        }

        #endregion

        #region IAmazonService Members

        /// <summary>
        /// Implementation of IAmazonService
        /// </summary>
        /// <param name="keyword">Search term to use.</param>
        /// <returns>Collection of movie titles.</returns>
        public IList<Title> Search(string keyword, bool withPlot)
        {
            return SearchWebService(keyword, 0, 0, true, null, withPlot);
        }

        /// <summary>
        /// Kill the connection to the web service.
        /// </summary>
        public void Dispose()
        {
            _AWSClient.Abort();
            _AWSClient.Close();
        }
        #endregion

        #region IAmazonService Members

        public AmazonSearchResult SearchWebService(string keyword, int pageNumber, int totalPages, bool firstRun, bool withPlot)
        {
            ItemSearchResponse response = GetResponse(keyword, pageNumber, withPlot);
            var amazonSearchResult = new AmazonSearchResult();
            amazonSearchResult.Titles = new List<Title>();

            if (response != null)
            {
                for (int x = 0; x < response.Items.Length; x++)
                {
                    if (firstRun)
                    {
                        amazonSearchResult.TotalPages = Convert.ToInt32(response.Items[x].TotalPages);
                    }
                    else
                    {
                        amazonSearchResult.TotalPages = totalPages;
                    }
                    amazonSearchResult.PageNumber = pageNumber;
                    if (response.Items[x] == null || response.Items[x].Item == null) continue;
                    for (int i = 0; i < response.Items[x].Item.Length; i++)
                    {
                        amazonSearchResult.Titles.Add(NewTitle(response, x, i));
                    }
                }
            }
            return amazonSearchResult;
        }

        public Title GetAmazonTitleByASIN(string asin)
        {
            ItemLookup itemLookup = new ItemLookup { AssociateTag = SubscriberID, AWSAccessKeyId = SubscriberID };

            const string index = "DVD";
            itemLookup.Shared = new ItemLookupRequest();
            //_AWSClient.ItemLookup()
            itemLookup.Shared.ItemId = new[] {asin};
            itemLookup.Shared.ResponseGroup = new[] { "ItemAttributes", "Images", "ItemIds", "EditorialReview" };
            //itemLookup.Shared.

            IList<Title> titles = new List<Title>();
            ItemLookupResponse itemLookupResponse = _AWSClient.ItemLookup(itemLookup);

            if (itemLookupResponse != null)
            {
                for (int x = 0; x < itemLookupResponse.Items.Length; x++)
                {
                    if (itemLookupResponse.Items[x] == null || itemLookupResponse.Items[x].Item == null) continue;

                    for (int i = 0; i < itemLookupResponse.Items[x].Item.Length; i++)
                    {
                        Title movieTitle = NewTitleByItem(itemLookupResponse, x, i);
                        titles.Add(movieTitle);
                    }
                }
            }

            return titles.FirstOrDefault();
        }

        #endregion
    }
}
